/** file FilterFactory.cpp
    author belot.nicolas
*/

#include "FilterFactory.h"
/*Check for include */
#ifdef HAVE_DLFCN_H
	#include <dlfcn.h>
#endif
#include <sys/types.h>
#ifdef HAVE_DIRENT_H
	#include <dirent.h>
	#define HAVE_DIR 1
#endif

#include <iostream>

#include "FilterTransfert.h"
#include "FilterDFT.h"
#include "FilterLP.h"
#include "FilterHP.h"
#include "FilterEqualizer.h"
#include "FilterCompressor.h"
#include "FilterConvolution.h"
#include "FilterFlanging.h"
#include "FilterOctave.h"
#include "FilterClipping.h"


namespace libtrckr {

map<string,FilterFactory::plugin> FilterFactory::byName;
map<Filter*,FilterFactory::plugin> FilterFactory::byInstance;

typedef const char** supported_t();
const string local[]={"FilterTransfert", "FilterDFT","FilterLP","FilterHP","FilterEqualizer","FilterCompressor","FilterConvolution","FilterFlanging","FilterOctave","FilterClipping", ""};

Filter* createNewFilter(const char* data){
	string name(data);
	std::cout <<"FilterType : " << name << std::endl;
	if(name=="FilterTransfert"){
		return new FilterTransfert();
	}else if(name=="FilterDFT"){
		return new FilterDFT();
	}else if(name=="FilterLP"){
		return new FilterLP();
	}else if(name=="FilterHP"){
		return new FilterHP();
	}else if(name=="FilterEqualizer"){
		return new FilterEqualizer();
	}else if(name=="FilterCompressor"){
		return new FilterCompressor();
	}else if(name=="FilterConvolution"){
		return new FilterConvolution();
	}else if(name=="FilterFlanging"){
		return new FilterFlanging();
	}else if(name=="FilterOctave"){
		return new FilterOctave();
	}else if(name=="FilterClipping"){
		return new FilterClipping();
	}else{
		std::cerr << "Unknow filter type" << std::endl;
	}
	return 0;

}

void DestroyFilter(Filter *f){
	std::cout << "Destroy Filter" << std::endl;
	delete f;
}

void** FilterFactory::handles=0;

int FilterFactory::init(string dirs[], int size){
	byName = map<string,plugin>();
	byInstance = map<Filter*,plugin>();
	handles=(void**)calloc(1024,sizeof(void*));
	int cnt=0;
	for(int i=0;i<size;i++){
		#ifdef HAVE_DIR
		DIR* dir=opendir(dirs[i].c_str());
		struct dirent * entry;
		while(entry=readdir(dir)){
			if( string(entry->d_name).find(".so") != string::npos){
				#ifdef HAVE_DLFCN_H
				void* handle=dlopen(string( dirs[i] + "/" + entry->d_name).c_str(), RTLD_LAZY);
				if(!handle){
					std::cerr << "Error plugins " << entry->d_name << " : " << dlerror() << std::endl;
					continue; 
				}
				// free error
				dlerror();
				supported_t *getSupported=(supported_t*)dlsym(handle,"getSupportedPlugins");
				char *dlerr = dlerror();
				if(dlerr){
					std::cerr << "Couldn't get supported plugin " << dlerr << std::endl;
					dlclose(handle);
					continue;
				}
				const char **list=getSupported();
				//Free error
				dlerror();
				create_t *create=(create_t*)dlsym(handle,"createFilter");
				dlerr = dlerror();
				if(dlerr){
					std::cerr << "Couldn't get creation function " << dlerr << std::endl;
					continue;
				}
;				dlerror();
				destroy_t *destroy=(destroy_t*)dlsym(handle,"destroyFilter");
				dlerr = dlerror();
				if(dlerr){
					std::cerr << "Couldn't get destruction function " << dlerr << std::endl;
					continue;
				}
				plugin a;
				a.cfunc=*create;
				a.dfunc=*destroy;
				for(int j=0;list[j]!=NULL;j++){
					byName.insert(pair<string,plugin>(list[j], a ));
				}
				handles[cnt]=handle;
				cnt++;
				#endif
			}
		}
		closedir(dir);
		#endif
	}
	for(int i=0; !local[i].empty();i++){
		plugin a;
		a.cfunc=createNewFilter;
		a.dfunc=DestroyFilter;
		byName.insert(pair<string,plugin>(local[i],a));
	}

}

void FilterFactory::close(){
	int cnt=0;
	while(handles[cnt]!=NULL){
		dlclose(handles[cnt]);
		cnt++;
	}
	free(handles);
}

void FilterFactory::deleteFilter(Filter *f){
	map<Filter*,plugin>::iterator it=byInstance.find(f);
	if(it!=byInstance.end()){
		plugin a=byInstance[f];
		a.dfunc(f);
		byInstance.erase(f);
	}else{
		std::cerr << "trying to free non existing filter" << std::endl;
	}
}

Filter *FilterFactory::loadNewFilter(string classe){
	map<string, plugin>::iterator it=byName.find(classe);
	if(it!=byName.end()){
		plugin a=byName[classe];//Should check that class exists..
		Filter *f=a.cfunc(classe.c_str());
		byInstance.insert(pair<Filter*,plugin>(f,a));
		return f;
	}
	return 0;
}




}
